# 引入库
import os
import time

import pyaudio
from math import sqrt
import numpy as np
import audioop

from multiprocessing import Process, Event, Queue, freeze_support

#############################################################
from modbus_tk import modbus_rtu
import serial

OS_ENV = os.getenv("OS_ENV", "local")

LIGHT_HEIGHT = 100
CHUNKS_PER_SECOND = 60
SAMPLE_RATE = int(str(os.getenv("SAMPLE_RATE", 44100)))  # 16000
CHUNK = int(str(os.getenv("CHUNK", int((1 / CHUNKS_PER_SECOND) * SAMPLE_RATE))))
INDEX_OF_CHOSEN_INPUT_DEVICE = int(str(os.getenv("INDEX_OF_CHOSEN_INPUT_DEVICE", 1)))
INDEX_OF_CHOSEN_OUTPUT_DEVICE = int(str(os.getenv("INDEX_OF_CHOSEN_OUTPUT_DEVICE", 1)))
MODE_PLAYER = os.getenv("MODE_PLAYER", "playback")
FORMAT = pyaudio.paInt16


class Audio:
    def __init__(self):
        # 设置属性
        self.pformat = FORMAT
        self.pchannels = 1
        # prate = 16000  # 采样率/Hz
        self.prate = SAMPLE_RATE  # 采样率/Hz
        self.input_device = INDEX_OF_CHOSEN_INPUT_DEVICE
        self.output_device = INDEX_OF_CHOSEN_OUTPUT_DEVICE
        self.chunk = CHUNK
        self.playback_que = Queue()
        self.beat_que = Queue()
        self.audio_que = Queue()
        self.event = Event()

    def is_beat(self, peak, threshold=80):
        return peak > threshold

    def getPeak(self, wav_data: str, type_str: str):
        peak = 0
        if type_str == "fft":
            waveform = np.frombuffer(wav_data, dtype=np.int16)
            fft_complex = np.fft.fft(waveform, n=CHUNK)
            # these operations can be factored out.
            # sqrt does not need to be computed for every distance
            max_val = sqrt(max(v.real * v.real + v.imag * v.imag for v in fft_complex))
            # print("max_val=", max_val)
            # factor out the scale multiply.
            scale_value = LIGHT_HEIGHT / max_val
            # print("scale_value=", scale_value)
            # for index and value in the frequencies
            for i, v in enumerate(fft_complex):
                # calculate the distance using the ecludian distance measure.
                dist = sqrt(v.real * v.real + v.imag * v.imag)
                peak = dist * scale_value
        elif type_str == "rms":
            peak = audioop.rms(wav_data, 2)

        return int(peak)

    def playback(self, eve: Event, que: Queue):
        print("playback...")
        audio = pyaudio.PyAudio()
        streamOut = audio.open(format=self.pformat,
                               channels=self.pchannels,
                               rate=self.prate,
                               output=True,
                               output_device_index=self.output_device,
                               frames_per_buffer=self.chunk)

        while True:
            if que.empty():
                eve.wait()
            else:
                data = que.get()
                streamOut.write(data)

    def listen(self, eve: Event, playback_que: Queue, beat_que: Queue, audio_que: Queue):
        print("listen...")
        print("self.pformat=", self.pformat)
        print("self.prate=", self.prate)
        print("self.chunk=", self.chunk)
        print("self.pchannels=", self.pchannels)

        audio = pyaudio.PyAudio()
        streamIn = audio.open(format=self.pformat,
                              channels=self.pchannels,
                              rate=self.prate,
                              input=True,
                              input_device_index=self.input_device,
                              frames_per_buffer=self.chunk)

        while True:
            audio_data = []
            audio_data = streamIn.read(self.chunk, exception_on_overflow=False)

            # peak = self.getPeak(audio_data, "fft")
            # if(peak>1):
            playback_que.put(audio_data)
            beat_que.put(audio_data)
            # tcp send messages
            pass
            # send over
            audio_que.put(audio_data)
            eve.set()

    def start(self):
        print("start")

        # 监听
        listen_process = Process(target=self.listen,
                                 args=(self.event, self.playback_que, self.beat_que, self.audio_que))
        listen_process.start()

        # 播放
        play_process = Process(target=self.playback, args=(self.event, self.playback_que))
        play_process.start()


class Light:
    def __init__(self):
        self.light = {}
        self.rs232_init()
        self.setColor({"S": 1, "R": 0, "G": 0, "B": 0})

    #  初始化蓝牙
    def rs232_init(self):
        self.serial = serial.Serial(port='/dev/ttyUSB0', baudrate=115200)
        print("rs232_init")

    def rs485_init(self):
        # RS485通信设置
        self.master = modbus_rtu.RtuMaster(serial.Serial(port='/dev/ttyAMA0',  # 连接端口
                                                         baudrate=9600,  # 连接波特率
                                                         bytesize=8,  # 数据位
                                                         parity='N',  # 奇偶校验位
                                                         stopbits=1))  # 停止位
        self.master.set_timeout(3.0)
        self.master.set_verbose(True)
        print("rs485_init")

    def setColor(self, colorValue: dict, protol="rs232"):
        self.light = colorValue

        checksum = '{:02x}'.format(
            int(self.light["S"]) + int(self.light["R"]) + int(self.light["G"]) + int(self.light["B"]))

        s = '{:02x}'.format(self.light["S"])
        r = '{:02x}'.format(self.light["R"])
        g = '{:02x}'.format(self.light["G"])
        b = '{:02x}'.format(self.light["B"])
        color = f'{s} {r} {g} {b} {checksum}'

        self.colorData = bytes.fromhex(color)
        print("data=", self.colorData)
        if (protol == "rs232"):
            self.serial.write(self.colorData)
        else:
            print("other protol")

    def setLightByRs485(self):
        #         outputValue = [0x03,0x00,0x01,0X64,0x64,0x00,0x00,0x00,0x00]
        outputValue1 = [0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00]

        time.sleep(0.1)
        self.master.execute(slave=0,  # 从机地址
                            function_code=0x10,  # 功能码
                            starting_address=0x0100,  # 寄存器开始地址
                            quantity_of_x=9,
                            output_value=outputValue1)
        time.sleep(0.1)

    def lightControlByAudio(self, audio):
        print("Light Control By Audio...")
        audioBeatQue = Queue()
        while True:
            audio.getAudioBeat(audioBeatQue)
            while not audioBeatQue.empty():
                isBeat = audioBeatQue.get()
                if (not isBeat):
                    data = bytes.fromhex('01 00 00 00 01')  # close
                    self.serial.write(data)
                else:
                    data = bytes.fromhex('01 60 00 00 61')  # open
                    self.serial.write(data)
                time.sleep(0.01)

    def start(self, audio):
        print("light start")
        # 氛围灯
        light_process = Process(target=self.lightControlByAudio, args=(audio,))
        light_process.start()


if __name__ == '__main__':
    ad = Audio()
    ad.start()

    # light = Light()
    # light.start(audio)










